﻿using System.Collections.Generic;
using System.Configuration;
using System.IO;
using EmbeddedSharp.Core;
using EmbeddedSharp.Core.TextConentProviders;

namespace Zove.Runtime.Core
{
    /// <summary>
    /// The default class loader for the framework
    /// </summary>
    public class DefaultClassLoader : IClassLoader
    {
        /// <summary>
        /// The default constructor
        /// </summary>
        public DefaultClassLoader(ILanguageNature languageNature)
        {
            ClassCache = new Dictionary<string, ZoomClass>();
            LanguageNature = languageNature;
        }

        /// <summary>
        /// The class cache
        /// </summary>
        protected Dictionary<string, ZoomClass> ClassCache { get; set; }

        /// <summary>
        /// Check if performance optimised tag is forced in the configuration
        /// </summary>
        public bool PerformanceOptimised
        {
            get
            {
                string value = ConfigurationManager.AppSettings["performance"];
                if (value != null && value == "true")
                {
                    return true;
                }

                return false;
            }
        }

        #region IClassLoader Members

        public IClassFinder ClassFinder { get; set; }

        /// <summary>
        /// Loading a class
        /// </summary>
        /// <param name="className">the name of the class</param>
        /// <returns>the zoom class</returns>
        public ZoomClass LoadByClassName(string className)
        {
            string fileName = LanguageNature.ClassFinder.GetLocation(className);
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ClassNotFoundException(className);
            }

            return LoadFromFile(fileName);
        }

        /// <summary>
        /// Loading a class from a 
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public ZoomClass LoadFromFile(string uri)
        {
            if (PerformanceOptimised)
            {
                if (ClassCache.ContainsKey(uri))
                {
                    return ClassCache[uri];
                }
            }

            string source = LoadSource(uri);
            ZoomClass zc = LoadFromSource(source);
            zc.Url = uri;
            if (PerformanceOptimised)
            {
                ClassCache.Add(uri, zc);
            }

            return zc;
        }

        /// <summary>
        /// Language nature
        /// </summary>
        public ILanguageNature LanguageNature
        {
            get;
            private set;
        }

        /// <summary>
        /// Load class from its source
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public ZoomClass LoadFromSource(string source)
        {
            IClassParser parser = LanguageNature.ClassParserFactory.CreateClassParser(new StringContentProvider(source));

            return parser.Parse();
        }

        #endregion

        /// <summary>
        /// Read the source from a file
        /// </summary>
        /// <param name="fileName">the file name</param>
        /// <returns></returns>
        public static string LoadSource(string fileName)
        {
            var fs = new FileStream(fileName, FileMode.Open);
            var reader = new StreamReader(fs);

            string source = reader.ReadToEnd();

            reader.Close();
            fs.Close();

            return source;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="className"></param>
        /// <param name="expr"></param>
        /// <returns></returns>
        public string ApplyRulesInStatement(
            string className, string expr)
        {
            var converter
                = new UserDefinedFunctionConverter(
                    className, ReplacingMode.StartingMatch);
            // if the function is declared in its class
            // place the class name in the front of the function call
            // to identify it
            expr = Utils.PrefixizeFunction(className, expr, LanguageNature.FunctionManager);
            ExpressionParser p
                = LanguageNature.ExpressionParserFactory.CreateParser(expr);
            
            Node root = p.Parse();
            expr = converter.Convert(root);

            return expr;
        }
    }
}